package push

import (
	"bytes"
	"config"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/anachronistic/apns"
	"github.com/garyburd/redigo/redis"
	"log"
	"net/http"
	"net/smtp"
	"rhelper"
	"strings"
	"text/template"
	"time"
)

const (
	PUSH_SERVER       = "http://msg.umeng.com/api/send"
	APP_MASTER_SECRET_ANDROID = "hsxnhbyo2bepblrldse85mk6lp9yedqz"
	APP_KEY_ANDROID           = "57aa834167e58e5001003266"
	APP_MASTER_SECRET_IOS = "g66mojvatazc76jxr3e6rle9jr1lfrlo"
	APP_KEY_IOS           = "57c67afee0f55a97a30012cd"
	MESSAGE_TYPE      = "FROM_LLWNAT_SERVER"
)

func SendGmail(to string, msgBody string) {
	type SmtpTemplateData struct {
		From    string
		To      string
		Subject string
		Body    string
	}

	var err error
	var doc bytes.Buffer

	// Go template
	const emailTemplate = `From: {{.From}}
		To: {{.To}}
		Subject: {{.Subject}}

		{{.Body}}

		Sincerely,

		{{.From}}
		`

	// Authenticate with Gmail (analagous to logging in to your Gmail account in the browser)
	auth := smtp.PlainAuth("", "crgri1052441607", "201209crg", "smtp.gmail.com")

	// Set the context for the email template.
	context := &SmtpTemplateData{"crgri1052441607 <crgri1052441607@gmail.com>",
		"Recipient Person <" + to + ">",
		msgBody,
		msgBody}

	// Create a new template for our SMTP message.
	t := template.New("emailTemplate")
	if t, err = t.Parse(emailTemplate); err != nil {
		log.Print("error trying to parse mail template ", err)
	}

	// Apply the values we have initialized in our struct context to the template.
	if err = t.Execute(&doc, context); err != nil {
		log.Print("error trying to execute mail template ", err)
	}

	// Actually perform the step of sending the email - (3) above
	err = smtp.SendMail("smtp.gmail.com:587",
		auth,
		"crgri1052441607",
		[]string{to},
		doc.Bytes())
	if err != nil {
		log.Print("ERROR: attempting to send a mail ", err)
	}
}

func creatUmengMessageAndroid(CustomMessage string, devToken string) ([]byte, error) {
	type UmengBody struct {
		Custom string `json:"custom"`
		Title  string `json:"title"`
		Text string `json:"text"`
		Ticker string `json:"ticker"`
		After_open string `json:"after_open"`
	}

	type UmengExtra struct {
		MessageType string `json:"messageType"`
	}

	type UmengPayload struct {
		DisplayType string    `json:"display_type"`
		Body        UmengBody `json:"body"`
		Extra UmengExtra `json:"extra"`
	}

	type UmengPolicy struct {
		ExpireTime string `json:"expire_time"`
	}

	type UmengMessage struct {
		Appkey          string       `json:"appkey"`
		Timestamp       string       `json:"timestamp"`
		ValidationToken string       `json:"validation_token"`
		Type            string       `json:"type"`
		DeviceToken     string       `json:"device_tokens"`
		Payload         UmengPayload `json:"payload"`
		Policy          UmengPolicy  `json:"policy"`
		Description     string       `json:"description"`
		ProductionMode  string       `json:"production_mode"`
	}

	body := UmengBody{}
	body.Custom = CustomMessage
	body.Title = MESSAGE_TYPE
	body.Text = CustomMessage
	body.Ticker = "1"
	body.After_open = "go_custom"

	extra := UmengExtra{}
	extra.MessageType = "homeMessage"

	payload := UmengPayload{}
	payload.DisplayType = "notification"
	payload.Body = body
	payload.Extra = extra

	umengPolicy := UmengPolicy{}
	umengPolicy.ExpireTime = ""

	umengMessage := UmengMessage{}
	umengMessage.Appkey = APP_KEY_ANDROID
	umengMessage.Timestamp = fmt.Sprintf("%d", int(time.Now().Unix()))

	validStr := strings.ToLower(umengMessage.Appkey) + strings.ToLower(APP_MASTER_SECRET_ANDROID) + umengMessage.Timestamp

	umengMessage.ValidationToken = fmt.Sprintf("%x", md5.Sum([]byte(validStr)))
	umengMessage.Type = "listcast"
	umengMessage.DeviceToken = devToken
	umengMessage.Payload = payload
	//umengMessage.Policy = umengPolicy
	umengMessage.Description = ""
	umengMessage.ProductionMode = "true"

	buf, err := json.Marshal(umengMessage)
	if err != nil {
		log.Println("encod err:", err)
		return nil, err
	}

	return buf, nil
}

func creatUmengMessageIOS(CustomMessage string, devToken string, badges string) ([]byte, error) {
	//type UmengAlert struct {
	//	Body string `json:"body"`
	//	Title  string `json:"title"`
	//	Subtitle string `json:"subtitle"`
	//}

	type UmengAps struct {
		Sound string `json:"sound"`
		//Badge int `json:"badge"`
		MutableContent int `json:"mutable-content"`
		Alert string `json:"alert"`
	}

	type UmengPayload struct {
		Aps        UmengAps `json:"aps"`
		MessageType string `json:"messageType"`
		Badge string `json:"badge"`
	}

	type UmengMessage struct {
		Appkey          string       `json:"appkey"`
		Timestamp       string       `json:"timestamp"`
		Type            string       `json:"type"`
		DeviceToken     string       `json:"device_tokens"`
		Payload         UmengPayload `json:"payload"`
		Description     string       `json:"description"`
		ProductionMode  string       `json:"production_mode"`
	}

	aps := UmengAps{}
	aps.Sound = "default"
	//aps.Badge = 1
	aps.MutableContent = 1
	aps.Alert = CustomMessage

	payload := UmengPayload{}
	payload.MessageType = "homeMessage"
	payload.Badge = badges
	payload.Aps = aps

	umengMessage := UmengMessage{}
	umengMessage.Appkey = APP_KEY_IOS
	umengMessage.Timestamp = fmt.Sprintf("%d", int(time.Now().Unix()))

	umengMessage.Type = "listcast"
	umengMessage.DeviceToken = devToken
	umengMessage.Payload = payload
	//umengMessage.Policy = umengPolicy
	umengMessage.Description = ""
	umengMessage.ProductionMode = "false"

	buf, err := json.Marshal(umengMessage)
	if err != nil {
		log.Println("encod err:", err)
		return nil, err
	}

	return buf, nil
}

//
//func sendUmengMessage(CustomMessage string, devToken string) {
//	msgBuf, err := creatUmengMessage(CustomMessage, devToken)
//	if err != nil {
//		log.Println("create umeng message err: ", err)
//		return
//	}
//
//	signStr := "POST" + PUSH_SERVER + string(msgBuf) + APP_MASTER_SECRET
//
//	url := PUSH_SERVER + "?sign=" + fmt.Sprintf("%x", md5.Sum([]byte(signStr)))
//	log.Println("url: ", url)
//
//	client := &http.Client{Timeout: time.Second * 60}
//	req, err := http.NewRequest("POST", url, bytes.NewBuffer(msgBuf))
//
//	resp, err := client.Do(req)
//	if err != nil {
//		log.Println("err: ", err)
//		return
//	}
//
//	if resp.StatusCode != 200 {
//		log.Println("resp.StatusCode: ", resp.StatusCode)
//	}
//
//	msgBody := make([]byte, 1024)
//	read, err := resp.Body.Read(msgBody)
//	if err != nil && read < 1 {
//		log.Println("read message body err: ", err)
//		return
//	}
//
//	msgBody = msgBody[:read]
//	log.Println("body: ", string(msgBody))
//}

func send2APNS(msg []byte, devtk string, alert string) {
	if devtk == "" {
		return
	}

	payload := apns.NewPayload()
	payload.Alert = alert
	payload.Badge = 1
	payload.Sound = "bingbong.aiff"

	pn := apns.NewPushNotification()
	pn.DeviceToken = devtk
	pn.AddPayload(payload)
	pn.Set("message", string(msg))

	certPath := fmt.Sprintf("%s%s", config.CertificatesDirectory, "/cert.pem")
	keyPath := fmt.Sprintf("%s%s", config.CertificatesDirectory, "/key-noenc.pem")

	log.Printf("send2APNS, certPath %s, keyPath:%s, apnsurl:%s, devtk:%s", certPath, keyPath, config.APNSUrl, devtk)
	client := apns.NewClient(config.APNSUrl, certPath, keyPath)

	resp := client.Send(pn)
	myAlert, _ := pn.PayloadString()
	log.Printf("  Alert: %s", myAlert)
	log.Printf("Success: %s", resp.Success)
	log.Printf("  Error: %s", resp.Error)
}

func loadApnsTkFromDB(account string) (tk string) {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	tk, err := redis.String(rconn.Do("HGET", rhelper.ApnsToken, account))
	if err != nil {
		log.Println("loadApnsTkFromDB failed, error:", err)
	}

	return
}

func send2ApnsWithLoadToken(account string, jsonMsg []byte, alert string) {

	tk := loadApnsTkFromDB(account)
	if tk == "" {
		return
	}

	send2APNS(jsonMsg, tk, alert)
}

func Send(accountList []string, msg string) {
	rconn := rhelper.Pool().Get()
	defer rconn.Close()

	var subscript = map[string]int{}

	//devTokenString := ""
	var os, devTokenStringIOS, devTokenStringAndroid string
	log.Println(accountList)
	for _, id := range accountList{
		//id, _ := redis.String(rconn.Do("HGET", rhelper.AccountHash+account, "id"))
		//if id == "" {
		//	return
		//}

		values, _ := redis.Strings(rconn.Do("HMGET", rhelper.UserHash+id, "os", "devToken"))
		os = values[0]
		devToken := values[1]

		if len(devToken) == 0{
			continue
		}

		if strings.Contains(os, "iOS") {
			devMessageLen, _ := redis.Int(rconn.Do("LLEN", rhelper.MsgURHash + ":" + "devMessage" + ":" + id))
			homeMessageLen, _ := redis.Int(rconn.Do("LLEN", rhelper.MsgURHash + ":" + "homeMessage" + ":" + id))

			subscript[id] = devMessageLen + homeMessageLen + 1
			//send2APNS(jsonMsg, devTokenString, alert)
			//sendUmengMessage(string(jsonMsg), devTokenString)
			if len(devTokenStringIOS) != 0{
				devTokenStringIOS += ","
			}

			devTokenStringIOS += devToken
		} else {
			if len(devTokenStringAndroid) != 0{
				devTokenStringAndroid += ","
			}

			//log.Println("devTokenString", devTokenString)
			devTokenStringAndroid += devToken
		}
	}

	subscriptJson, _ := json.Marshal(subscript)

	if len(devTokenStringIOS) != 0{
		sendUmengIOS(msg, devTokenStringIOS, string(subscriptJson))
	}

	if len(devTokenStringAndroid) != 0{
		sendUmengAndroid(msg, devTokenStringAndroid)
	}
}

func sendUmengAndroid(json string, devToken string){
	msgBuf, err := creatUmengMessageAndroid(json, devToken)
	if err != nil {
		log.Println("create umeng message err: ", err)
		return
	}

	log.Println(devToken)

	signStr := "POST" + PUSH_SERVER + string(msgBuf) + APP_MASTER_SECRET_ANDROID

	url := PUSH_SERVER + "?sign=" + fmt.Sprintf("%x", md5.Sum([]byte(signStr)))
	log.Println("url: ", url)

	client := &http.Client{Timeout: time.Second * 60}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(msgBuf))

	resp, err := client.Do(req)
	if err != nil {
		log.Println("err: ", err)
		return
	}

	if resp.StatusCode != 200 {
		log.Println("resp.StatusCode: ", resp.StatusCode)
	}

	msgBody := make([]byte, 1024)
	read, err := resp.Body.Read(msgBody)
	if err != nil && read < 1 {
		log.Println("read message body err: ", err)
		return
	}

	msgBody = msgBody[:read]
	log.Println("body: ", string(msgBody))
}

func sendUmengIOS(msg string, devToken string, badges string){
	msgBuf, err := creatUmengMessageIOS(msg, devToken, badges)
	if err != nil {
		log.Println("create umeng message err: ", err)
		return
	}

	log.Println(devToken)

	signStr := "POST" + PUSH_SERVER + string(msgBuf) + APP_MASTER_SECRET_IOS

	url := PUSH_SERVER + "?sign=" + fmt.Sprintf("%x", md5.Sum([]byte(signStr)))
	log.Println("url: ", url)

	client := &http.Client{Timeout: time.Second * 60}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(msgBuf))

	resp, err := client.Do(req)
	if err != nil {
		log.Println("err: ", err)
		return
	}

	if resp.StatusCode != 200 {
		log.Println("resp.StatusCode: ", resp.StatusCode)
	}

	msgBody := make([]byte, 1024)
	read, err := resp.Body.Read(msgBody)
	if err != nil && read < 1 {
		log.Println("read message body err: ", err)
		return
	}

	msgBody = msgBody[:read]
	log.Println("body: ", string(msgBody))
}

func SendLogoutMessage(jsonMsg []byte, title string, devToken string, os string) {
	if devToken == "" {
		return
	}

	if strings.Contains(os, "iOS") {
		send2APNS(jsonMsg, devToken, title)
	} else {
		//sendUmengMessage(string(jsonMsg), devToken)
	}
}
